package cn.tedu.charging.device.startup;

import cn.tedu.charging.device.dao.repository.StationRepository;
import cn.tedu.charging.device.pojo.vo.StationPO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ApplicationRunner 让我们应用在启动的时候 做一些事情
 */
@Slf4j
@Component
public class StationDataInit implements ApplicationRunner {

    @Autowired
    private StationRepository repository;

    @Autowired
    private RedisTemplate<String, Serializable> redisTemplate;

    @PostConstruct
    public void init(){
        log.debug("PostConstruct init");
    }

    /**
     * 缓存预热 数据同步 数据一致性的问题
     * 每次重启设备服务，都会走 StationDataInit#run方法
     * 1
     * 从数据库查询充电站的信息放到redis里
     * 1 通过Redis-GEO 实现位置查询， 每个充点站的经纬度定义成redis的point 点 存入redis geo 数据类型中
     * 2 查询性能很快
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.debug("缓存预热,从数据库加载充电站信息开始...");
        log.debug("查询充电站数据 todo 不要直接查询所有数据 应该通过分页查询:{}");
        List<StationPO> stationPOList = repository.listStation(); //注意考虑数据库表的行数 要分页,1000W 数据太大 内存放不下 内存溢出
        log.debug("查询充电站数据成功:{}",stationPOList);

        Map<String,Point> points = new HashMap<>();

        BoundGeoOperations stations =
                redisTemplate.boundGeoOps("stations");

        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
        for (StationPO stationPO : stationPOList) {
            //获取场站的经纬度
            BigDecimal stationLng = stationPO.getStationLng();
            BigDecimal stationLat = stationPO.getStationLat();

            String id = String.valueOf(stationPO.getId());
            //1创建GEO point 点 存入redis
            Point  point = new Point(Double.parseDouble(stationLng.toEngineeringString()),
                    Double.parseDouble(stationLat.toString()));
            points.put(id,point);
            //2 把充电站的详情放入redis，用什么数据类型呢？ 是为了方便的通过id来查询

            //String key value                     Map<String,String>  可以  key：站的id value 站的详情
            //Set  map 套set set 放的是不重复的value  Map<String,Set<>>  key 站的id value set 放一个 站的id对应的站详情
            //Hash map 套map map 可以放重复 map结构        Map<String,Map<String,value>>
            //List map 套list List 可以放重复下标index   Map<String,List<>>
            //Zset map 套set set 放的是不重复的value并且可以排序 Map<String,TreeSet<>>

            //距离1 返回了2个站id 怎么能通过2个站的id快速的查询出站id对应的站详情
            //距离5 返回了3个站id 怎么能通过3个站的id快速的查询出 站id对应的站详情
            //答 String Hash
            // Map<String,value>   key：站的id value 站的详情
           /* Map<String,Object> temp0 = new HashMap<>();
            temp0.put("1","json{名称:高级充电站,桩数量:100,枪数量:200}");*/

            // Map<String,Map<String,value>>
            // 大key,小key,value 怎么用 怎么存,怎么取  大key 站id 小key 站属性 value 属性值
          /*  Map<String,Map<String,Object>> temp = new HashMap<>();
            Map<String,Object> smallMap = new HashMap<>();
            smallMap.put("名称","高级充电站");
            smallMap.put("桩数量",100);
            smallMap.put("枪数量",200);
            temp.put("1",smallMap);*/

            // 大key,小key,value 怎么用 怎么存,怎么取  大key Stations 小key 站id  value 站详情
           /* Map<String,Map<String,Object>> temp1 = new HashMap<>();
            Map<String,Object> smallMap1 = new HashMap<>();
            smallMap1.put("站id1","json{名称:高级充电站,桩数量:100,枪数量:200}");
            smallMap1.put("站id2","json{名称:高级充电站1,桩数量:110,枪数量:220}");
            temp1.put("所有充电站",smallMap1);*/

            //大key
            String stationId = String.valueOf(stationPO.getId());
            Map<String,Object> smallMap  = new HashMap<>();
            smallMap.put("name",stationPO.getStationName());
            smallMap.put("code",stationPO.getStationCode());
            smallMap.put("deviceNumber",stationPO.getDeviceNumber());
            hashOperations.putAll(stationId,smallMap);
        }
        stations.add(points);
    }
}
